/*
Wav File Writer Filter for Windows Mobile
Copyright (C) 2008 syu5.
*/

#pragma once

#include "WavWriterIF.h"

#define FILTER_NAME _T("WavWriter")

const AMOVIESETUP_MEDIATYPE sudPinTypes={
    &MEDIATYPE_Audio,
    &MEDIASUBTYPE_None
};

const AMOVIESETUP_PIN sudPins =
{
    L"",
    TRUE,
    TRUE,
    FALSE,
    FALSE,
    &CLSID_NULL,
    0,
    1,
    &sudPinTypes
};

const AMOVIESETUP_FILTER afFilterInfo=
{
    &__uuidof(CWavWriter),
    FILTER_NAME,     
    MERIT_DO_NOT_USE,
    1,               
    &sudPins         
};

// Filter class
class CWavWriter : public CBaseRenderer, public IFileSinkFilter {
	ULONG m_ulSizeOfWfx; 
	DWORD m_dwWavSize;
	WAVEFORMATEX *m_pWfx;
	HANDLE m_File;
	CCritSec m_CritSec;
	TCHAR m_FileName[MAX_PATH];
	static const LPSTR s_RIFF;
	static const LPSTR s_WAVE;
	static const LPSTR s_fmt;
	static const LPSTR s_data;
public:
	DECLARE_IUNKNOWN
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) {
		CWavWriter *pNewFilter = new CWavWriter(pUnk, phr);
		if (pNewFilter == NULL) {
			*phr = E_OUTOFMEMORY;
		}
		return pNewFilter;
	}
	CWavWriter(LPUNKNOWN pUnk,HRESULT *phr) :
		CBaseRenderer(__uuidof(CWavWriter),FILTER_NAME,pUnk,phr)
		, m_pWfx(NULL), m_ulSizeOfWfx(0), m_dwWavSize(0), m_File(INVALID_HANDLE_VALUE) 
	{
		ZeroMemory(m_FileName, sizeof(TCHAR)*MAX_PATH);
	}
	virtual ~CWavWriter() {
		if(m_pWfx)
			delete[] m_pWfx;
	}
#ifdef DEBUG
	STDMETHODIMP_(ULONG) NonDelegatingRelease() {
		return CUnknown::NonDelegatingRelease();
	}
#endif
	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv) {
		CheckPointer(ppv,E_POINTER);
		if(riid==IID_IFileSinkFilter) {
			return GetInterface((LPUNKNOWN)dynamic_cast<IFileSinkFilter*>(this), ppv);
		}
		return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
	}

	HRESULT CheckMediaType(const CMediaType *pMediaType) {
		CAutoLock mylock(&m_CritSec);
		if(!IsEqualGUID(pMediaType->majortype, MEDIATYPE_Audio) ||
		   !IsEqualGUID(pMediaType->subtype, MEDIASUBTYPE_PCM)) {
			return S_FALSE;
		}
		if(!IsEqualGUID(*pMediaType->FormatType()
			, FORMAT_WaveFormatEx)) {
			return S_FALSE;
		}
		WAVEFORMATEX *pWfx=(WAVEFORMATEX *)pMediaType->Format();
		m_ulSizeOfWfx=pMediaType->FormatLength();
		if(m_pWfx)
			delete[] m_pWfx;
		m_pWfx=(WAVEFORMATEX *)new BYTE[m_ulSizeOfWfx];
		CopyMemory(m_pWfx,pWfx,m_ulSizeOfWfx);
		return S_OK;
	}
	HRESULT DoRenderSample(IMediaSample *pMediaSample) {
		CAutoLock mylock(&m_CritSec);
		if(m_File == INVALID_HANDLE_VALUE) {
			return S_OK;
		}
		LPBYTE pData;
		DWORD dwSize,dwWritten;
		pMediaSample->GetPointer(&pData);
		dwSize=pMediaSample->GetActualDataLength();
		WriteFile(m_File, pData, dwSize, &dwWritten, NULL);
		m_dwWavSize += dwSize;
		return S_OK;
	}
	HRESULT OnStartStreaming(void) {
		CAutoLock mylock(&m_CritSec);
		m_File = CreateFile(m_FileName
			, GENERIC_WRITE, 0, NULL 
			, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if(m_File == INVALID_HANDLE_VALUE) {
			return E_FAIL;
		}
		DWORD dwZero = 0; // dummy
		DWORD dwWritten;
		WriteFile(m_File, s_RIFF, strlen(s_RIFF), &dwWritten, NULL);
		WriteFile(m_File, &dwZero, sizeof(DWORD), &dwWritten, NULL); // dummy
		WriteFile(m_File, s_WAVE, strlen(s_WAVE), &dwWritten, NULL);
		WriteFile(m_File, s_fmt , strlen(s_fmt),  &dwWritten, NULL);
		WriteFile(m_File, &m_ulSizeOfWfx, sizeof(DWORD), &dwWritten, NULL);
		WriteFile(m_File, m_pWfx, m_ulSizeOfWfx, &dwWritten, NULL);
		WriteFile(m_File, s_data, strlen(s_data), &dwWritten, NULL);
		WriteFile(m_File, &dwZero, sizeof(DWORD), &dwWritten, NULL);
		return S_OK;
	}
	HRESULT OnStopStreaming(void) {
		CAutoLock mylock(&m_CritSec);
		if(m_File == INVALID_HANDLE_VALUE) {
			return S_OK;
		}
		// after "RIFF"
		SetFilePointer(m_File, strlen(s_RIFF), NULL, FILE_BEGIN);
		DWORD dwSize, dwWritten;
		dwSize=sizeof(DWORD)+strlen(s_WAVE)+strlen(s_fmt)+sizeof(DWORD)+
			m_ulSizeOfWfx+strlen(s_data)+m_dwWavSize;
		WriteFile(m_File, &dwSize, sizeof(DWORD), &dwWritten, NULL);
		// after "data"
		SetFilePointer(m_File, 20+m_ulSizeOfWfx+4, NULL, FILE_BEGIN);
		dwSize=m_dwWavSize;
		WriteFile(m_File, &dwSize, sizeof(DWORD), &dwWritten, NULL);
		CloseHandle(m_File);
		m_File=INVALID_HANDLE_VALUE;
		return S_OK;
	}
	// IFileSinkFilter
	HRESULT SetFileName(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt) {
		if(wcslen(pszFileName) > MAX_PATH)
			return ERROR_FILENAME_EXCED_RANGE;
		size_t len=1+lstrlenW(pszFileName);
		HRESULT hr=StringCchCopyW(m_FileName, len, pszFileName);
		return hr;
	}
	HRESULT GetCurFile(LPOLESTR* ppszFileName, AM_MEDIA_TYPE *pmt) {
		HRESULT hr=NOERROR;
		size_t len = 1+lstrlenW(m_FileName);
		*ppszFileName = (LPOLESTR)
		QzTaskMemAlloc(sizeof(WCHAR) * (len));
		if (*ppszFileName != NULL) 
		{
			hr=StringCchCopyW(*ppszFileName, len, m_FileName);
		}
		if(pmt) 
		{
			ZeroMemory(pmt, sizeof(*pmt));
			pmt->majortype = MEDIATYPE_Audio;
			pmt->subtype = MEDIASUBTYPE_None;
		}
		return hr;
	}
};
